package com.leaf.u.basehttp.download;

import android.util.Log;

import com.activeandroid.query.From;
import com.activeandroid.query.Select;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DownloadManager<T extends DownloadModel> {
    // ThreadPoolExecutor
    private static final int MAX_IMUM_POOL_SIZE = 100; // 最大线程池的数量
    private static final int KEEP_ALIVE_TIME = 1; // 存活的时间
    private static final TimeUnit UNIT = TimeUnit.SECONDS; // 时间单位
    private int corePoolSize = 3;
    private ThreadPoolExecutor executor;
    private Map<String, TaskHandler<T>> tasks = new HashMap<>();
    private Map<String, TaskHandler<T>> taskUnfinish = new HashMap<>();
    private Map<String, TaskHandler<T>> taskfinish = new HashMap<>();
    private Map<String, TaskHandler<T>> StandbyTask = new HashMap<>();
    private Map<String, TaskHandler<T>> taskenqueue = new HashMap<>();
    private Map<String, TaskHandler<T>> taskpause = new HashMap<>();
    private String downloadPath;
    private Timer timer = new Timer(true);
    private IHttp ihttp;
    private List<DownloadListener<T>> listeners = new ArrayList<DownloadListener<T>>();
    private Map<String, DownloadListener<T>> mapListeners = new HashMap<String, DownloadListener<T>>();
    private DownloadConfig mConfig;
    private ConnectionType mConnectionType;
    // private

    enum TaskState {
        ADD, DEL, LOAD, FINISH, PAUSE, STANDBY, NetChange, CONNECTCONFIRM, FAILURE
    }

    public enum ConnectionType {
        TYPE_MOBILE, TYPE_WIFI, TYPE_OTHER, TYPE_NONE;
    }

    public void setDownloadConfig(DownloadConfig config) {
        this.mConfig = config;
    }

    public DownloadConfig getDownloadConfig() {
        return mConfig;
    }

    public void onNetWorkChange(ConnectionType type) {
        mConnectionType = type;
        Log.e("DownloadManager", "onNetWorkChange");
        if (mConfig.networkType() == DownloadConfig.NETWORK_WIFI && type != ConnectionType.TYPE_WIFI) {
            stopActiveTask();
        } else if (type != ConnectionType.TYPE_NONE) {
            startActiveTask();
        } else {
            stopActiveTask();
        }
        if (!taskenqueue.isEmpty()) {
            notifyObservers(TaskState.NetChange, null);
        }
    }

    public DownloadManager(Class<T> clz) {
        ihttp = new OkHttpImpl();
        executor = new ExecutorService(corePoolSize, MAX_IMUM_POOL_SIZE, KEEP_ALIVE_TIME, UNIT, new PriorityBlockingQueue<Runnable>());
        try {
            Select select = new Select();
            From from = select.from(clz);
            List<T> list = from.execute();
            for (T model : list) {
                String url = model.getUrl();
                TaskHandler<T> handler = new TaskHandler<T>(new OkHttpImpl(), this, model);
                tasks.put(url, handler);
                State state = handler.get().getStatus();
                if (state == State.SUCCESS) {
                    taskfinish.put(url, handler);
                } else {
                    taskUnfinish.put(url, handler);
                    if (state == State.PAUSE) {
                        taskpause.put(url, handler);
                    }
                }
            }
        } catch (Exception e) {

        }

    }

    public List<TaskHandler<T>> getOnLoading() {
        List<TaskHandler<T>> list = new ArrayList<TaskHandler<T>>();
        Collection<TaskHandler<T>> values = taskUnfinish.values();
        list.addAll(values);
        return list;
    }

    public List<TaskHandler<T>> getFinished() {
        List<TaskHandler<T>> list = new ArrayList<TaskHandler<T>>();
        Collection<TaskHandler<T>> values = taskfinish.values();
        list.addAll(values);
        return list;
    }

    public List<TaskHandler<T>> getAll() {
        List<TaskHandler<T>> list = new ArrayList<TaskHandler<T>>();
        Collection<TaskHandler<T>> values = tasks.values();
        list.addAll(values);
        return list;
    }

    public void addListener(String tag, DownloadListener<T> l) {
        DownloadListener<T> tDownloadListener = mapListeners.get(tag);
        if (tDownloadListener != null) {
            listeners.remove(tDownloadListener);
        }
        mapListeners.put(tag, l);
        listeners.add(l);
    }

    public void addListener(DownloadListener<T> l) {
        if (l == null) {
            throw new NullPointerException("listener == null");
        }
        synchronized (this) {
            if (!listeners.contains(l))
                listeners.add(l);
        }
    }

    public void deleteListener(DownloadListener<T> l) {
        listeners.remove(l);
    }

    public void configMax(int max) {
        executor.setCorePoolSize(max);
    }

    public int getMaximumPoolSize() {
        return executor.getCorePoolSize();
    }

    public void configDownloadPath(String path) {
        this.downloadPath = path;
    }

    public List<TaskHandler<T>> getDownloadList() {
        List<TaskHandler<T>> list = new ArrayList<TaskHandler<T>>();
        Collection<TaskHandler<T>> values = tasks.values();
        list.addAll(values);
        return list;
    }

    public Timer getTimer() {
        return timer;
    }

    public void notifyObservers(TaskState type, TaskHandler<T> data) {
        int size = 0;
        DownloadListener<T>[] arrays = null;
        synchronized (this) {
            // if (hasChanged()) {
            // clearChanged();
            size = listeners.size();
            arrays = new DownloadListener[size];
            listeners.toArray(arrays);
            // }s

            switch (type) {
                case ADD:
                    tasks.put(data.getUrl(), data);
                    taskUnfinish.put(data.getUrl(), data);
                    taskpause.remove(data.getUrl());
                    break;
                case DEL:
                    tasks.remove(data.getUrl());
                    taskUnfinish.remove(data.getUrl());
                    taskfinish.remove(data.getUrl());
                    taskpause.remove(data.getUrl());
                    break;
                case FINISH:
                    taskUnfinish.remove(data.getUrl());
                    taskfinish.put(data.getUrl(), data);
                    break;
                case STANDBY:
                    boolean b = StandbyTask.containsKey(data.getUrl());
                    if (b && data.get().getStatus() == State.STANDBY) {
                        StandbyTask.remove(data.getUrl());
                        addTask(data.get());
                    }
                    break;
                case PAUSE:
                    taskpause.put(data.getUrl(), data);
                    break;
                case FAILURE:
                    taskenqueue.put(data.getUrl(), data);
                    break;
                case LOAD:
                    taskpause.remove(data.getUrl());
                    break;
            }

            if (arrays != null) {
                for (DownloadListener<T> observer : arrays) {
                    // observer.update(type, data);
                    switch (type) {
                        case ADD:
                            observer.onAddTask(taskUnfinish.size(), tasks.size(), data.get());
                            break;
                        case DEL:
                            observer.onDelTask(taskUnfinish.size(), tasks.size(), data.get());
                            break;
                        case PAUSE:
                            observer.onPauseTask(data.get());
                            break;
                        case FINISH:
                            observer.onFinish(taskfinish.size(), tasks.size(), data.get());
                            break;
                        case LOAD:
                            observer.onTaskLoading(taskUnfinish.size(), data.get());
                            break;
                        case NetChange:
                            observer.onNetChange(mConnectionType);
                            break;
                        case CONNECTCONFIRM:
                            observer.onConnect(mConnectionType, data.get());
                            break;
                        case FAILURE:
                            observer.onFailure(data.get());
                            break;
                        default:
                            break;
                    }
                    observer.onDownloadTaskCount(taskUnfinish.size() - taskpause.size());
                }
            }
        }
    }

    public TaskHandler<T> addTask(T request) {
//        if (mConfig.networkType() == DownloadConfig.NETWORK_WIFI && mConnectionType != ConnectionType.TYPE_WIFI) {
//            TaskHandler<T> task = null;
//            if (tasks.containsKey(request.getUrl())) {
//                task = tasks.get(request.getUrl());
//            } else {
//                task = new TaskHandler<>(ihttp, this, request);
//            }
//            notifyObservers(TaskState.CONNECTCONFIRM, task);
//            return task;
//        }
        if (request == null) {
            throw new NullPointerException();
        }
        if (tasks.containsKey(request.getUrl())) {
            TaskHandler<T> taskHandler = tasks.get(request.getUrl());
            T model = taskHandler.get();
            State status = model.getStatus();
            if (status == State.LOADING || status == State.START) {
                return taskHandler;
            }
            //如果任务是挂起状态则放入等待容器
            if (status == State.SUSPENDED) {
                taskHandler.get().setStatus(State.STANDBY);
                StandbyTask.put(taskHandler.getUrl(), taskHandler);
                return taskHandler;
            }
            taskHandler.get().setStatus(State.ENQUEUE);
//            if (executor.getQueue().contains(taskHandler)) {
//            }
            executor.remove(taskHandler);
            taskHandler.notifyListener(State.ENQUEUE);
            executor.execute(taskHandler);
            notifyObservers(TaskState.ADD, taskHandler);
            return taskHandler;
        }//
        if (request.getAbsolutePath() == null && downloadPath == null) {
            throw new IllegalArgumentException("you must config the download path ");
        }
        if (request.getAbsolutePath() == null && request.getFilename() == null) {
            throw new IllegalArgumentException("you must config the download path ");
        }
        if (request.getAbsolutePath() == null) {
            long millis = System.currentTimeMillis();
            request.setAbsolutePath(downloadPath + File.separator + millis + request.getFilename());
        }
        request.setStatus(State.ENQUEUE);
        TaskHandler<T> handler = new TaskHandler<T>(ihttp, this, request);
        handler.notifyListener(State.ENQUEUE);
        executor.execute(handler);
        notifyObservers(TaskState.ADD, handler);
        return handler;
    }

    void delTask(TaskHandler<T> handler) {
        executor.remove(handler);
        notifyObservers(TaskState.DEL, handler);
    }

    //请求暂停还没开始并且已经在队列中的任务时应当调用
    void remove(TaskHandler<T> handler) {
        executor.remove(handler);
    }

    public void startAllTask() {
        List<TaskHandler<T>> onLoading = getOnLoading();
        for (TaskHandler<T> task : onLoading) {
            addTask(task.get());
        }
    }

    public void startActiveTask() {
        List<TaskHandler<T>> list = new ArrayList<>();
        Collection<TaskHandler<T>> values = taskenqueue.values();
        list.addAll(values);
        for (TaskHandler<T> task : list) {
            task.start();
        }
//        taskenqueue.clear();
    }

    public void stopActiveTask() {
        List<TaskHandler<T>> onLoading = getOnLoading();
        for (TaskHandler<T> task : onLoading) {
            if (task.get().getStatus() == State.ENQUEUE ||
                    task.get().getStatus() == State.LOADING ||
                    task.get().getStatus() == State.STANDBY ||
                    task.get().getStatus() == State.START) {
                taskenqueue.put(task.getUrl(), task);
            }
            task.stop();
        }
    }

    public void stopAllTask() {
        List<TaskHandler<T>> onLoading = getOnLoading();
        for (TaskHandler<T> task : onLoading) {
            task.stop();
        }
    }

    public TaskHandler<T> getTask(String url) {
        return tasks.get(url);
    }

}
